/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: signal api
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2022-05-29     JasonHu           Init
 */

#include <nxos/syscall.h>
#include <nxos/signal.h>
#include <nxos/msgqueue.h>
#include <nxos/process.h>

NX_Error NX_SignalSend(NX_U32 tid, NX_Signal signal, void * signalValue)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall3(NX_API_SignalSend, tid, signal, signalValue)));
    return err;
}

NX_Error NX_SignalGetAttr(NX_Signal signal, NX_SignalAttr * outAttr)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall2(NX_API_SignalGetAttr, signal, outAttr)));
    return err;
}

NX_Error NX_SignalSetAttrReal(NX_Signal signal, NX_SignalAttr * attr)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall2(NX_API_SignalSetAttr, signal, attr)));
    return err;
}

NX_Error NX_SignalControl(NX_Signal signalFirst, NX_Signal signalLast, NX_U32 cmd)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall3(NX_API_SignalContorl, signalFirst, signalLast, cmd)));
    return err;
}

NX_Error NX_SignalInitAttr(NX_SignalAttr * attr, NX_SignalHandler handler, NX_U32 flags)
{
    if (!attr)
    {
        return NX_EINVAL;
    }
    attr->handler = handler;
    attr->flags = flags;
    return NX_EOK;
}

NX_Error NX_SignalThread(NX_Solt thread, NX_Solt msgQueue)
{
    NX_Error err;
    NX_ErrorSet((err = NX_Syscall2(NX_API_SignalThread, thread, msgQueue)));
    return err;
}

/**
 * Only support one thread helper now!
 */
NX_PRIVATE NX_Solt msgSolt = NX_SOLT_INVALID_VALUE;

NX_PRIVATE NX_U32 UserSignalHelper(void *arg)
{
    NX_MsgBuf buf;
    NX_SignalMsg msg;
    buf.payload = (char *)&msg;
    buf.len = sizeof(msg);
    while (NX_True)
    {
        /* block recv msg */
        if (NX_MsgQueueRecv(msgSolt, &buf) == NX_EOK)
        {
            if (msg.attr.handler != NX_NULL)
            {
                msg.attr.handler(&msg.info);
            }
        }
    }
    return 0;
}

/**
 * Create a user helper thread for signals
 */
NX_PRIVATE NX_Error CreateSignalThread(void)
{
    NX_Error err;

    if (msgSolt != NX_SOLT_INVALID_VALUE)
    {
        return NX_EOK;
    }

    msgSolt = NX_MsgQueueCreate(sizeof(NX_SignalMsg), 32);
    if (msgSolt == NX_SOLT_INVALID_VALUE)
    {
        return NX_ENOMEM;
    }

    NX_Solt thread = NX_ThreadCreate(NX_NULL, UserSignalHelper, NX_NULL, 0);
    if (thread == NX_SOLT_INVALID_VALUE)
    {
        NX_MsgQueueDestroy(msgSolt);
        msgSolt = NX_SOLT_INVALID_VALUE;
        return NX_ENOMEM;
    }

    /* set user signal handler */
    err = NX_SignalThread(NX_ThreadGetCurrent(), msgSolt);
    if (err != NX_EOK)
    {
        NX_MsgQueueDestroy(msgSolt);
        NX_ThreadTerminate(thread, 0);
        return err;
    }
    return NX_EOK;
}

NX_Error NX_SignalSetAttr(NX_Signal signal, NX_SignalAttr * attr)
{
    /* Check if need create user thread */
    if (attr->handler != NX_SIGNAL_HANDLER_DEFAULT &&
        attr->handler != NX_SIGNAL_HANDLER_IGNORE)
    {
        int retry = 10;
        do 
        {
            if (CreateSignalThread() == NX_EOK)
            {
                break;
            }
        } while (--retry > 0);
    }
    return NX_SignalSetAttrReal(signal, attr);
}
